﻿/*
 * Created by SharpDevelop.
 * User: Christian Pacella
 * Date: 2010/10/16
 * Time: 01:29 AM
 */
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Windows.Forms;

using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using Emgu.CV.UI;
using Emgu.Util;

namespace Crianthis.FaceDetective
{
	/// <summary>
	/// Description of MainForm.
	/// </summary>
	public partial class MainForm : Form
	{
		private static string FolderPath        = string.Empty;
		private static string MyDocumentsFolder = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
		private const int imageWidth            = 64;
		private const int imageHeight           = 64;
		
		public MainForm()
		{
			InitializeComponent();
		}
		
		#region Image Methods
		
		private void FillListView(string folderPath)
		{
            toolStripStatusLabel.Text = "Adding images...";
            Application.DoEvents();
            
			DirectoryInfo dir = new DirectoryInfo(folderPath);
			List<string> fileList = new List<string>();
			int fileCount = dir.GetFiles().Length;
			int currentFile = 0;
			
			if (fileCount > 0)
			{
				this.toolStripProgressBar.Minimum = 0;
				this.toolStripProgressBar.Maximum = fileCount - 1;
				
	            foreach (FileInfo file in dir.GetFiles())
	            {
	                try
	                {
	                	currentFile++;
	                	toolStripStatusLabel.Text = string.Format("Adding {0} to the ImageList...", file.Name);
					    Application.DoEvents();
					    this.imageList.Images.Add(ScaleImage(Image.FromFile(file.FullName)));
	                    fileList.Add(file.FullName);
	                    this.toolStripProgressBar.Value = currentFile;
					    Application.DoEvents();
	                }
	                catch
	                {
	                	// The file was not an image file...
	                }
	            }
	
				this.toolStripProgressBar.Minimum = 0;
				this.toolStripProgressBar.Maximum = imageList.Images.Count - 1;

				this.imageList.ColorDepth = ColorDepth.Depth32Bit;
	            this.imageList.ImageSize = new Size(imageWidth, imageHeight);
	            
	            this.listView.BeginUpdate();
	            this.listView.View = View.LargeIcon;
	            this.listView.LargeImageList = this.imageList;
	
	            for (int j = 0; j < this.imageList.Images.Count; j++)
	            {
	                ListViewItem item = new ListViewItem();
	
	                item.ImageIndex = j;
	                item.Text = Path.GetFullPath(fileList[j]);
	                
	                toolStripStatusLabel.Text = string.Format("Added {0} to the ListView...", Path.GetFileName(fileList[j]));
				    Application.DoEvents();
	
	                this.listView.Items.Add(item);
                    this.toolStripProgressBar.Value = j;
	            }
	            
	            this.listView.EndUpdate();
                toolStripStatusLabel.Text = "Done adding images...";
			    Application.DoEvents();
			}
		}
		
		private void DetectFaces()
		{
            toolStripStatusLabel.Text = "Detecting faces...";
            Application.DoEvents();
            
            int images = this.listView.Items.Count;
            this.toolStripProgressBar.Maximum = images;
            
            if (images > 0)
            {
                string destinationFolderPath = string.Format("{0}\\{1}\\{2}\\Faces\\{3}", 
                                                             MyDocumentsFolder, 
                                                             Application.CompanyName, 
                                                             Application.ProductName, 
                                                             DateTime.Now.ToString("yyyyMMddHHmmssffff"));
                
                // Create directory structure.
                if (!Directory.Exists(destinationFolderPath))
                {
                    Directory.CreateDirectory(destinationFolderPath);
                    Directory.CreateDirectory(destinationFolderPath + "\\Faces");
                    Directory.CreateDirectory(destinationFolderPath + "\\Thumbs");
                }
                
                int imagesProcessed = 0;
                int faceCount = 0;
                
                // Start the stopwatch.
                Stopwatch stopWatch = Stopwatch.StartNew();
                
                foreach (ListViewItem item in this.listView.Items)
	            {
                    imagesProcessed++;
                    this.toolStripProgressBar.Value = imagesProcessed;
                    Application.DoEvents();
                    
                    try
	                {
    				    string sourceFilepath = item.Text;
    				    string sourceFilename = Path.GetFileNameWithoutExtension(item.Text);
    				    string destinationFilename = string.Format("{0}\\Thumbs\\Image_{1}_{2}_thumbnail{3}", 
    				                                               destinationFolderPath, 
    				                                               imagesProcessed.ToString(), 
    				                                               sourceFilename, 
    				                                               Path.GetExtension(item.Text));
    				    
    				    SavePhoto(sourceFilepath, destinationFilename, imageWidth);
    				    
                        Image<Bgr, Byte> image = new Image<Bgr, Byte>(sourceFilepath); //Read the files as an 8-bit Bgr image
                        Image<Gray, Byte> gray = image.Convert<Gray, Byte>(); //Convert it to Grayscale 
                        
                        //normalizes brightness and increases contrast of the image
                        gray._EqualizeHist();
                        
                        //Read the HaarCascade objects
                        HaarCascade face = new HaarCascade("haarcascade_frontalface_alt_tree.xml");
                        
                        //Detect the faces from the gray scale image and store the locations as rectangle
                        //The first dimensional is the channel
                        //The second dimension is the index of the rectangle in the specific channel
                        MCvAvgComp[][] facesDetected = gray.DetectHaarCascade(
                            face, 1.1, 10, Emgu.CV.CvEnum.HAAR_DETECTION_TYPE.DO_CANNY_PRUNING, new Size(20, 20));
            
                        int currentImageFaceCount = 0;
                        foreach (MCvAvgComp faceFound in facesDetected[0])
                        {
                            faceCount++;
                            currentImageFaceCount++;
        	                toolStripStatusLabel.Text = string.Format("Processing Image #{0} ({1}) - {2} faces detected ({3} total detected)...", 
                                                                      imagesProcessed.ToString(), 
                                                                      sourceFilename, 
                                                                      currentImageFaceCount.ToString(), 
                                                                      faceCount.ToString());
        				    Application.DoEvents();
                            
                            // extract the cropped face
                            string fileName = string.Format("{0}\\Faces\\Image_{1}_{2}_Face_{3}.jpg", 
                                                            destinationFolderPath, 
                                                            imagesProcessed.ToString(), 
                                                            sourceFilename, 
                                                            currentImageFaceCount.ToString());

                            Bitmap bitmap = new Bitmap(image.ToBitmap());
                            Bitmap saveBitmap = new Bitmap(image.ToBitmap());
                            saveBitmap = bitmap.Clone(faceFound.rect, bitmap.PixelFormat);
                            saveBitmap.Save(fileName, System.Drawing.Imaging.ImageFormat.Bmp);
        	                toolStripStatusLabel.Text = string.Format("Image #{0} ({1}) - {2} faces saved ({3} total saved)...", 
                                                                      imagesProcessed.ToString(), 
                                                                      sourceFilename, 
                                                                      currentImageFaceCount.ToString(), 
                                                                      faceCount.ToString());
        				    Application.DoEvents();
                        }
	                }
                    catch
	                {

	                }
	            }
	                
                // Clear the progress bar
                this.toolStripProgressBar.Value = 0;

                // Stop the stopwatch.
                stopWatch.Stop();
                        
                // Get the elapsed time as a TimeSpan value.
                TimeSpan ts = stopWatch.Elapsed;
        
                // Format and display the TimeSpan value.
                string elapsedTime = String.Format("{0:00} hrs, {1:00} min, {2:00} sec and {3:00} ms",
                    ts.Hours, ts.Minutes, ts.Seconds,
                    ts.Milliseconds / 10);
                
                string statusMessage = string.Empty;
                
                if (imagesProcessed == 0)
                {
                    // No images processed.
                    statusMessage = "Done! No images were processed.";
                }
                else if (imagesProcessed > 1)
                {
                    // More than one image processed
                    if (faceCount == 0)
                    {
                        // No faces detected.
                        statusMessage = string.Format("Done! {0} images were processed and no faces were detected.", 
                                                      imagesProcessed);
                    }
                    else if (faceCount > 1)
                    {
                        // More than one face detected.
                        statusMessage = string.Format("Done! {0} images were processed and {1} faces were detected in {2}.", 
                                                      imagesProcessed, 
                                                      faceCount, 
                                                      elapsedTime);
                    }
                    else if (faceCount == 1)
                    {
                        // One face detected.
                        statusMessage = string.Format("Done! {0} images were processed and one face was detected in {1}.", 
                                                      imagesProcessed, 
                                                      elapsedTime);
                    }
                }
                else if (imagesProcessed == 1)
                {
                    // One image processed.
                    if (faceCount == 0)
                    {
                        // No faces detected.
                        statusMessage = "Done! One image was processed and no faces were detected.";
                    }
                    else if(faceCount > 1)
                    {
                        // More than one face detected.
                        statusMessage = string.Format("Done! One image was processed and {0} faces were detected in {1}.", 
                                                      faceCount, 
                                                      elapsedTime);
                    }
                    else if (faceCount == 1)
                    {
                        // One face detected.
                        statusMessage = string.Format("Done! One image was processed and one face was detected in {0}.", 
                                                      elapsedTime);
                    }
                }
                
                toolStripStatusLabel.Text = statusMessage;
                MessageBox.Show(statusMessage, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
		}
		
        private Bitmap ScaleImage(Image oldImage) 
        { 
            double resizeFactor = 1; 
        
            if (oldImage.Width > imageWidth || oldImage.Height > imageWidth) 
            { 
                double widthFactor = Convert.ToDouble(oldImage.Width) / imageWidth; 
                double heightFactor = Convert.ToDouble(oldImage.Height) / imageWidth; 
                resizeFactor = Math.Max(widthFactor, heightFactor); 
        
            } 
            int width = Convert.ToInt32(oldImage.Width / resizeFactor); 
            int height = Convert.ToInt32(oldImage.Height / resizeFactor); 
            Bitmap newImage = new Bitmap(width, height); 
            Graphics g = Graphics.FromImage(newImage); 
            g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; 
            g.DrawImage(oldImage, 0, 0, newImage.Width, newImage.Height); 
            return newImage; 
        } 

        private bool SavePhoto(string sourceFilename, string destinationFilename, int thumbWidth)
    	{
    		System.Drawing.Image imgTmp = null;
    		System.Drawing.Bitmap imgFoto = null;
    		double sf = 0;
    		
    		imgTmp = System.Drawing.Image.FromFile(sourceFilename);
    		if ((imgTmp.Width > thumbWidth)) {
    			sf = imgTmp.Width / thumbWidth;
    			imgFoto = new System.Drawing.Bitmap(thumbWidth, Convert.ToInt32(imgTmp.Height / sf));
    			Rectangle recDest = new Rectangle(0, 0, thumbWidth, imgFoto.Height);
    			Graphics gphCrop = Graphics.FromImage(imgFoto);
    			gphCrop.SmoothingMode = SmoothingMode.HighQuality;
    			gphCrop.CompositingQuality = CompositingQuality.HighQuality;
    			gphCrop.InterpolationMode = InterpolationMode.High;
    
    			gphCrop.DrawImage(imgTmp, recDest, 0, 0, imgTmp.Width, imgTmp.Height, GraphicsUnit.Pixel);
    		} else {
    		    imgFoto = new Bitmap(imgTmp);
    		}

    		System.Drawing.Imaging.Encoder myEncoder = null;
    		System.Drawing.Imaging.EncoderParameter myEncoderParameter = null;
    		System.Drawing.Imaging.EncoderParameters myEncoderParameters = null;
    
    		System.Drawing.Imaging.ImageCodecInfo[] arrayICI = System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders();
    		System.Drawing.Imaging.ImageCodecInfo jpegICI = null;
    		int x = 0;
    		for (x = 0; x <= arrayICI.Length - 1; x++) {
    			if ((arrayICI[x].FormatDescription.Equals("JPEG"))) {
    				jpegICI = arrayICI[x];
    				break; // TODO: might not be correct. Was : Exit For
    			}
    		}
    		myEncoder = System.Drawing.Imaging.Encoder.Quality;
    		myEncoderParameters = new System.Drawing.Imaging.EncoderParameters(1);
    		myEncoderParameter = new System.Drawing.Imaging.EncoderParameter(myEncoder, 60L);
    		myEncoderParameters.Param[0] = myEncoderParameter;
    		imgFoto.Save(destinationFilename, jpegICI, myEncoderParameters);
    		imgFoto.Dispose();
    		imgTmp.Dispose();
    		return true;
    	}
		
		#endregion

		#region Form Controls

		private void ResetFormControls()
		{
            // Clear the imagelist and listview
            listView.Items.Clear();
            imageList.Images.Clear();
            
            // Set the status bar label
            toolStripStatusLabel.Text = "Ready";
   		}

		private void startToolStripButton_Click(object sender, EventArgs e)
		{
			// Show a Browse for Folder dialog and only process images
			// if the user clicks the OK button.
            if (folderBrowserDialog.ShowDialog() == DialogResult.OK)
			{
            	FolderPath = folderBrowserDialog.SelectedPath;
            	
            	// The user clicked OK, so show the wait cursor
	            this.Cursor = Cursors.WaitCursor;
	            
	            // Reset the controls
	            ResetFormControls();
	            	            
			    // Fill the listview control
			    FillListView(folderBrowserDialog.SelectedPath);
        
			    // Detect faces.
			    DetectFaces();

			    // Return the cursor to the default pointer
	            this.Cursor = Cursors.Default;
			}
		}

        private void aboutToolStripButton_Click(object sender, EventArgs e)
		{
		    AboutForm formAbout = new AboutForm();
		    formAbout.ShowDialog();
		}
        
		#endregion
	}
}
